/**
 ******************************************************************************
 * @file           : bsp_wwdg.c
 * @brief          : WWDG Driver Implementation (STM32H7xx)
 ******************************************************************************
 * @attention
 *
 * 窗口看门狗驱动程序实现文件
 * WWDG特性：
 * - 窗口限制：只能在特定时间窗口内喂狗
 * - 早期唤醒中断：计数器递减到特定值时产生中断
 * - APB1时钟：时钟源来自APB1
 * 
 * WWDG vs IWDG：
 * - WWDG：有时间窗口限制，适用于严格的任务调度
 * - IWDG：无时间窗口，只要不超时即可，更灵活
 *
 ******************************************************************************
 */

/* Includes ------------------------------------------------------------------*/
#include "./BSP/WWDG/bsp_wwdg.h"
#include "./BSP/WWDG/bsp_wwdg_config.h"

/* Private includes ----------------------------------------------------------*/
/* Private typedef -----------------------------------------------------------*/
/* Private define ------------------------------------------------------------*/
/* Private macro -------------------------------------------------------------*/
/* Private variables ---------------------------------------------------------*/
static WWDG_HandleTypeDef sg_wwdg_handle;           /*!< WWDG句柄 */
static volatile WWDG_State_e sg_wwdg_state = WWDG_STATE_RESET;  /*!< WWDG当前状态 */
static volatile uint8_t sg_wwdg_initialized = 0;    /*!< 初始化标志 */

/* Private function prototypes -----------------------------------------------*/
static void wwdg_msp_init(void);

/* Private functions ---------------------------------------------------------*/

/**
 * @brief       WWDG MSP初始化
 *
 * @param       无
 *
 * @retval      无
 *
 * @note        使能WWDG时钟和中断
 */
static void wwdg_msp_init(void)
{
    /* 使能WWDG时钟 */
    __HAL_RCC_WWDG_CLK_ENABLE();
    
    /* 配置WWDG中断优先级并使能 */
    HAL_NVIC_SetPriority(WWDG_IRQn, 0, 0);  /* 最高优先级 */
    HAL_NVIC_EnableIRQ(WWDG_IRQn);
}

/* Exported functions --------------------------------------------------------*/

/**
 * @brief       WWDG初始化
 *
 * @param       无
 *
 * @retval      无
 *
 * @note        配置WWDG预分频、计数器初值、窗口值
 * @note        初始化完成后WWDG自动启动，必须定期喂狗
 * @note        WWDG一旦启动无法停止，直到系统复位
 */
void wwdg_init(void)
{
    if (sg_wwdg_initialized)
    {
        log_w("WWDG已经初始化");
        return;
    }
    
    /* MSP初始化 */
    wwdg_msp_init();
    
    /* 配置WWDG参数 */
    sg_wwdg_handle.Instance = WWDG;
    sg_wwdg_handle.Init.Prescaler = WWDG_PRESCALER;         /* 预分频器 */
    sg_wwdg_handle.Init.Window = WWDG_WINDOW_VALUE;         /* 窗口值 */
    sg_wwdg_handle.Init.Counter = WWDG_COUNTER_VALUE;       /* 计数器初值 */
    sg_wwdg_handle.Init.EWIMode = WWDG_EWI_ENABLE;          /* 启用早期唤醒中断 */
    
    /* 初始化WWDG */
    if (HAL_WWDG_Init(&sg_wwdg_handle) != HAL_OK)
    {
        log_e("WWDG初始化失败");
        sg_wwdg_state = WWDG_STATE_ERROR;
        return;
    }
    
    sg_wwdg_state = WWDG_STATE_READY;
    sg_wwdg_initialized = 1;
    
    log_i("WWDG初始化完成");
    log_i("  预分频器: %d", WWDG_PRESCALER);
    log_i("  计数器初值: %d", WWDG_COUNTER_VALUE);
    log_i("  窗口值: %d", WWDG_WINDOW_VALUE);
    log_w("WWDG已启动，必须定期喂狗！");
}

/**
 * @brief       WWDG反初始化
 *
 * @param       无
 *
 * @retval      无
 *
 * @note        WWDG一旦启动无法停止，此函数仅用于清除软件状态
 * @note        实际的WWDG仍在运行，直到系统复位
 */
void wwdg_deinit(void)
{
    if (!sg_wwdg_initialized)
    {
        return;
    }
    
    /* 禁用中断 */
    HAL_NVIC_DisableIRQ(WWDG_IRQn);
    
    /* 清除软件状态 */
    sg_wwdg_state = WWDG_STATE_RESET;
    sg_wwdg_initialized = 0;
    
    log_w("WWDG反初始化（硬件仍在运行）");
}

/**
 * @brief       WWDG喂狗
 *
 * @param       无
 *
 * @retval      无
 *
 * @note        必须在计数器值<窗口值时喂狗
 * @note        过早喂狗会导致系统复位
 * @note        过晚喂狗（计数器<64）也会导致系统复位
 */
void wwdg_feed(void)
{
    if (!sg_wwdg_initialized)
    {
        log_e("WWDG未初始化");
        return;
    }
    
    /* 刷新计数器到初值 */
    if (HAL_WWDG_Refresh(&sg_wwdg_handle) != HAL_OK)
    {
        log_e("WWDG喂狗失败");
        sg_wwdg_state = WWDG_STATE_ERROR;
        return;
    }
    
    log_d("WWDG喂狗成功");
}

/**
 * @brief       WWDG刷新计数器（指定值）
 *
 * @param[in]   counter 计数器值（64~127）
 *
 * @retval      无
 *
 * @note        必须在窗口范围内刷新
 * @note        counter必须>窗口值，否则触发复位
 */
void wwdg_refresh(uint8_t counter)
{
    if (!sg_wwdg_initialized)
    {
        log_e("WWDG未初始化");
        return;
    }
    
    if (counter < 64 || counter > 127)
    {
        log_e("计数器值无效: %d（必须在64~127之间）", counter);
        return;
    }
    
    /* 直接写入计数器 */
    WRITE_REG(sg_wwdg_handle.Instance->CR, (WWDG_CR_WDGA | counter));
    
    log_d("WWDG刷新计数器: %d", counter);
}

/**
 * @brief       获取WWDG状态
 *
 * @param       无
 *
 * @retval      WWDG_State_e 当前状态
 *
 * @note        用于状态监控和调试
 */
WWDG_State_e wwdg_get_state(void)
{
    return sg_wwdg_state;
}

/**
 * @brief       获取当前计数器值
 *
 * @param       无
 *
 * @retval      计数器值（64~127）
 *
 * @note        可用于判断是否需要喂狗
 */
uint8_t wwdg_get_counter(void)
{
    if (!sg_wwdg_initialized)
    {
        return 0;
    }
    
    return (uint8_t)(READ_REG(sg_wwdg_handle.Instance->CR) & WWDG_CR_T);
}

/**
 * @brief       检查WWDG是否已初始化
 *
 * @param       无
 *
 * @retval      1: 已初始化  0: 未初始化
 *
 * @note        用于防止重复初始化
 */
uint8_t wwdg_is_initialized(void)
{
    return sg_wwdg_initialized;
}

/**
 * @brief       打印WWDG配置信息
 *
 * @param       无
 *
 * @retval      无
 *
 * @note        显示预分频、计数器、窗口值、超时时间等
 */
void wwdg_print_config(void)
{
    if (!sg_wwdg_initialized)
    {
        log_w("WWDG未初始化");
        return;
    }
    
    uint8_t counter = wwdg_get_counter();
    const char *state_str[] = {"RESET", "READY", "BUSY", "ERROR"};
    
    log_i("===== WWDG配置信息 =====");
    log_i("状态: %s", state_str[sg_wwdg_state]);
    log_i("预分频器: %d", WWDG_PRESCALER);
    log_i("计数器初值: %d", WWDG_COUNTER_VALUE);
    log_i("窗口值: %d", WWDG_WINDOW_VALUE);
    log_i("当前计数器: %d", counter);
    log_i("=======================");
}

/**
 * @brief       WWDG早期唤醒中断回调函数
 *
 * @param[in]   hwwdg WWDG句柄指针
 *
 * @retval      无
 *
 * @note        在计数器递减到0x40时触发
 * @note        用户可以在此函数中添加自定义处理
 */
void HAL_WWDG_EarlyWakeupCallback(WWDG_HandleTypeDef *hwwdg)
{
    /* 清除早期唤醒中断标志 */
    __HAL_WWDG_CLEAR_FLAG(hwwdg, WWDG_FLAG_EWIF);
    
    log_w("WWDG早期唤醒中断！计数器即将到达最小值");
    
    /* 用户可以在此添加自定义处理 */
    /* 例如：记录日志、触发告警、执行紧急操作等 */
}

/**
 * @brief       WWDG中断服务函数
 *
 * @param       无
 *
 * @retval      无
 *
 * @note        此函数需要在stm32h7xx_it.c中调用
 * @note        void WWDG_IRQHandler(void) { HAL_WWDG_IRQHandler(&sg_wwdg_handle); }
 */
void WWDG_IRQHandler(void)
{
    HAL_WWDG_IRQHandler(&sg_wwdg_handle);
}
